package com.lfy.commons.aspect;

import com.lfy.common.constant.RedisConstant;
import com.lfy.common.enums.LimitEnum;
import com.lfy.common.result.R;
import com.lfy.commons.anno.limit.CurrentLimit;
import com.lfy.commons.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
 *  限流切面
 *
 * @author <a href="https://www.lvfeiy.top">吕飞扬</a>
 * @date 2023/10/16 21:35
 */
@Aspect
@Component
@Slf4j
@Order(998)
public class AspectLimit {
    @Pointcut("@annotation(com.lfy.commons.anno.limit.CurrentLimitContaion) || @annotation(com.lfy.commons.anno.limit.CurrentLimit)")
    public void cut(){

    }
    @Around(value = "cut()", argNames = "joinPoint")
    public Object limit(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        CurrentLimit[] annotationsByType = method.getAnnotationsByType(CurrentLimit.class);

        String methodUrl = joinPoint.getSignature().getDeclaringType().toString().concat(".").concat(joinPoint.getSignature().getName()).concat("()");
        for (int i = 0; i < annotationsByType.length; i++) {
            CurrentLimit currentLimit = annotationsByType[i];
            //策略
            String strategy = currentLimit.strategy().toDo();
            //时间
            int time = currentLimit.time();
            //次数
            int once = currentLimit.once();
            //转秒
            long second = toSecond(currentLimit, time);
            long now = System.currentTimeMillis();
            String key = RedisConstant.LIMIT_CONSTANT.concat(currentLimit.strategy().name().concat("@")).
                    concat(strategy).concat(" index@".concat(String.valueOf(i).concat(" - "))).concat(methodUrl);

            RedisUtil.expire(key,second,TimeUnit.SECONDS);
            //删除时间之外的key
            RedisUtil.zRemoveRangeByScore(key,0,now-second*1000);
            Long zCard = RedisUtil.zCard(key);
            if(zCard != null && zCard >= once){
                //TODO 黑名单
                log.info(" {} ",key);
                return R.error(LimitEnum.LIMIT);
            }
            RedisUtil.zAdd(key,now,now);
        }



        return joinPoint.proceed();

    }

    private long toSecond(CurrentLimit currentLimit, int time) {
        TimeUnit unit = currentLimit.nuit();
        return  unit.toSeconds(time);
    }
}
